package org.slicerj.actions;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IEditorActionDelegate;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.MessageConsole;
import org.eclipse.ui.console.MessageConsoleStream;
import org.eclipse.ui.part.FileEditorInput;
import org.slicerj.SlicerJActivator;
import org.slicerj.core.SliceExecutor;
import org.slicerj.core.SliceMarker;
import org.slicerj.core.SlicingCiterion;
import org.slicerj.utils.MethodVisitor;

import de.unisb.cs.st.javaslicer.common.classRepresentation.Instruction;
import de.unisb.cs.st.javaslicer.slicing.SliceInstructionsCollector;
import de.unisb.cs.st.javaslicer.slicing.Slicer;
import de.unisb.cs.st.javaslicer.slicing.SlicingCriterion;
import de.unisb.cs.st.javaslicer.traceResult.ThreadId;
import de.unisb.cs.st.javaslicer.traceResult.TraceResult;

public class GenerateSlicesAction extends AbstractUIAction implements
		IEditorActionDelegate {
	private IEditorPart editor;
	private IFile currentFile;
	private IFolder traceFolder;
	private List<SliceMarker> sliceMarkerList = new ArrayList<SliceMarker>();
	private static List<Instruction> insList = new ArrayList<Instruction>(); 

	@Override
	public void run(IAction action) {

		SlicingCiterion slicingCiterion = SlicerJActivator.getDefault()
				.getSlicingCiterion();

		int slicingPoint = slicingCiterion.getSlicigPoint();

		IJavaElement je = JavaUI.getEditorInputJavaElement(editor
				.getEditorInput());

		CompilationUnit cu = parse(editor);
		createFolder(getProject(), "traces");

		if (je instanceof ICompilationUnit) {
			MethodVisitor locator = new MethodVisitor(slicingPoint);
			cu.accept(locator);

			String pkgName = locator.getPackageName();
			String className = locator.getClassName();
			String method = locator.getMethodName();

			slicingCiterion.setPackageName(pkgName);
			slicingCiterion.setClassName(className);
			slicingCiterion.setMethodName(method);

			try {
				SliceExecutor slicer = new SliceExecutor(pkgName, className,
						method, slicingPoint, slicingCiterion.toString());
				File traceFile = traceFolder.getFile(className).getLocation()
						.toFile();

				String criterionStr = slicingCiterion.toString();

				System.out.println("Slicing criterion:" + criterionStr);
				System.out.println("retrieving instructions:");

				final List<Instruction> slices = getSlice(traceFile, method,
						criterionStr);

				MessageConsole msgConsole = new MessageConsole("Slicer", null);
				MessageConsoleStream outConsole = msgConsole.newMessageStream();
				ConsolePlugin.getDefault().getConsoleManager()
						.addConsoles(new IConsole[] { msgConsole });

				if (slices.size() == 0) {
					System.out.println("The slice yeilded no instruction(s)!");
				} else {
					int prevLineNo = -1;

					StringBuilder opCode = new StringBuilder();
					int cnt = 0;
					for (int i = 0; i < slices.size(); i++) {

						Instruction slice = slices.get(i);

						String c = slice.getMethod().getReadClass().getName();
						String m = slice.getMethod().getName();

						String sliceInfo = SlicerJActivator.getDefault()
								.getWorkspacePath().toString()
								+ " " + c + "." + m + "()";
						String slicePkgPath = c.replace('.', '/');

						String absFileLocation = "src/" + slicePkgPath
								+ ".java";
						System.out.println("full: " + absFileLocation);

						outConsole.println(sliceInfo + " : "
								+ slice.getLineNumber() + " : " + slice);

						if (prevLineNo != slice.getLineNumber()) {
							opCode.append(slice + " ");
							System.out.println("x:" + slice.toString());
							sliceMarkerList.add(new SliceMarker(c + "." + m
									+ "()", slice.getLineNumber(),
									slicePkgPath, m, slice.toString()));
							cnt++;
							prevLineNo = slice.getLineNumber();
						} else {
							sliceMarkerList.get(cnt - 1).appendOpcode(
									slice.toString());
						}
					}
					createMarker();

				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void selectionChanged(IAction action, ISelection selection) {

	}

	@Override
	public void setActiveEditor(IAction action, IEditorPart targetEditor) {
		editor = targetEditor;
		if (editor != null) {
			this.currentFile = ((FileEditorInput) (editor.getEditorInput()))
					.getFile();
			SlicerJActivator.getDefault().setCurrentProject(
					currentFile.getProject());
		}
	}

	private CompilationUnit parse(IEditorPart editor) {
		ICompilationUnit cu = JavaCore.createCompilationUnitFrom(currentFile);
		ASTParser parser = ASTParser.newParser(AST.JLS3);
		parser.setSource(cu);

		return (CompilationUnit) parser.createAST(null);
	}

	public IProject getProject() {
		return currentFile.getProject();
	}

	public void createFolder(IProject project, String folderLocation) {
		traceFolder = project.getFolder(folderLocation);
		if (!traceFolder.exists()) {
			try {
				traceFolder.create(true, true, null);
				System.out.println(folderLocation + " folder has been created");
			} catch (CoreException ce) {
				ce.printStackTrace();
				System.err
						.println("Delegate.createFolder(): cannot create folder @: "
								+ folderLocation);
			}
		}
	}

	protected static List<Instruction> getSlice(File traceFile, String thread,
			String criterion) throws IllegalArgumentException, IOException,
			URISyntaxException, InterruptedException {

		TraceResult trace = TraceResult.readFrom(traceFile);

		List<SlicingCriterion> sc = SlicingCriterion.parseAll(criterion,
				trace.getReadClasses());

		ThreadId threadId = null;
		for (ThreadId t : trace.getThreads()) {
			if (thread.equals(t.getThreadName())) {
				threadId = t;
				break;
			}
		}

		Slicer slicer = new Slicer(trace);
		SliceInstructionsCollector collector = new SliceInstructionsCollector();
		slicer.addSliceVisitor(collector);
		slicer.process(threadId, sc, true);
		Set<Instruction> slice = new TreeSet<Instruction>(
				collector.getDynamicSlice());
		insList = new ArrayList<Instruction>(slice);
		return new ArrayList<Instruction>(slice);
	}
	
	public List<Instruction> getInstructions(){
		return insList;
	}
	public void createMarker() {
		IFile file;

		for (int i = 0; i < sliceMarkerList.size(); i++) {
			SliceMarker sliceMarker = sliceMarkerList.get(i);

			String absFileLocation = "src/" + sliceMarker.getLocation()
					+ ".java";

			file = SlicerJActivator.getDefault().getCurrentProject()
					.getFile(absFileLocation);
			IMarker marker = null;
			try {
				marker = file.createMarker(IMarker.BOOKMARK);

				marker.setAttribute(IMarker.MESSAGE,
						sliceMarker.getMethodDescription());

				int lineNumber = sliceMarker.getLineNumber();
				lineNumber = (lineNumber == -1) ? 1 : lineNumber++;

				marker.setAttribute(IMarker.LINE_NUMBER,
						sliceMarker.getLineNumber());
			} catch (CoreException ce) {
				ce.printStackTrace();
			}
		}
	}
	
	public List<SliceMarker> getMarkers(){
		return sliceMarkerList;
	}
}